use crate::data_channels::{message::Message, request::request_response};
use model_graph_common::{
    utils::{new_uuid, rrg01_home_path, write_to_file},
    G01_TEMP_PATH,
};
use model_graph_types::channel::{BatchScriptRequest, CliRequest, CliResponse, CommandResult};
use std::{
    collections::HashMap,
    process::{Command, Stdio},
};
use tokio::time::{sleep, timeout, Duration};

pub async fn cli_request(
    peer_id: &String,
    request: CliRequest,
    timeout: u64,
) -> anyhow::Result<CliResponse> {
    //
    tracing::debug!("请求节点:{}", peer_id);

    let request_bytes = bincode::serialize(&request)
        .map_err(|err| anyhow::anyhow!("{}", err))?
        .to_vec();

    let message = Message::cli_request_msg(request_bytes);

    //发送
    tracing::debug!("开始发送请求:{}", message.data.len());
    let response_message = request_response(&peer_id, message, timeout).await?;
    tracing::debug!("获取请求数据:{}", response_message.data.len());

    //
    let response = bincode::deserialize::<CliResponse>(&response_message.data[..])
        .map_err(|err| anyhow::anyhow!("{}", err))?;

    Ok(response)
}

pub async fn batch_script_request(
    peer_id: &String,
    request: BatchScriptRequest,
    timeout: u64,
) -> anyhow::Result<CommandResult> {
    //
    tracing::debug!("请求节点:{}", peer_id);

    let request_bytes = bincode::serialize(&request)
        .map_err(|err| anyhow::anyhow!("{}", err))?
        .to_vec();

    let message = Message::batch_script_request_msg(request_bytes);

    //发送
    tracing::debug!("开始发送请求:{}", message.data.len());
    let response_message = request_response(&peer_id, message, timeout).await?;
    tracing::debug!("获取请求数据:{}", response_message.data.len());

    //
    let response = bincode::deserialize::<CommandResult>(&response_message.data[..])
        .map_err(|err| anyhow::anyhow!("{}", err))?;

    Ok(response)
}

pub async fn local_execute_cli_message_request(
    request_message: Message,
) -> anyhow::Result<Message> {
    //
    let request: CliRequest = bincode::deserialize::<CliRequest>(&request_message.data[..])
        .map_err(|err| anyhow::anyhow!("{}", err))?;

    let response = local_execute_cli_request(request).await?;

    let response_bytes = bincode::serialize(&response)
        .map_err(|err| anyhow::anyhow!("{}", err))?
        .to_vec();

    let message = Message::response(response_bytes);

    Ok(message)
}

pub async fn local_execute_batch_script_message_request(
    request_message: Message,
) -> anyhow::Result<Message> {
    //
    let request: BatchScriptRequest =
        bincode::deserialize::<BatchScriptRequest>(&request_message.data[..])
            .map_err(|err| anyhow::anyhow!("{}", err))?;

    let response = local_execute_batch_script_request(request).await?;

    let response_bytes = bincode::serialize(&response)
        .map_err(|err| anyhow::anyhow!("{}", err))?
        .to_vec();

    let message = Message::response(response_bytes);

    Ok(message)
}

pub async fn local_execute_cli_request(request: CliRequest) -> anyhow::Result<CliResponse> {
    //env
    let env = if let Some(env) = &request.env {
        env.clone()
    } else {
        HashMap::new()
    };
    let CliRequest {
        current,
        commands,
        timeout,
        stdout,
        stderr,
        ..
    } = request;

    let mut results: Vec<CommandResult> = vec![];

    for command in commands {
        let result = _execute_command(&env, &current, command, &timeout, &stdout, &stderr).await?;
        results.push(result);
    }

    Ok(CliResponse {
        results,
        extensions: HashMap::new(),
    })
}

pub async fn local_execute_batch_script_request(
    request: BatchScriptRequest,
) -> anyhow::Result<CommandResult> {
    //env
    let env = if let Some(env) = &request.env {
        env.clone()
    } else {
        HashMap::new()
    };
    let BatchScriptRequest {
        current,
        script,
        timeout,
        stdout,
        stderr,
        ..
    } = request;

    _execute_batch_script(&env, &current, script, &timeout, &stdout, &stderr).await
}

async fn _execute_command(
    env: &HashMap<String, String>,
    current: &Option<String>,
    command_args: Vec<String>,
    time_out: &Option<u32>,
    stdout: &Option<bool>,
    stderr: &Option<bool>,
) -> anyhow::Result<CommandResult> {
    if command_args.len() == 0 {
        return Err(anyhow::anyhow!("命令为空!"));
    }
    let mut command = if cfg!(target_os = "windows") {
        Command::new("cmd")
    } else {
        Command::new("sh")
    };
    //env
    for (key, val) in env {
        command.env(key, val);
    }
    //当前目录
    if let Some(cur_dir) = current {
        command.current_dir(cur_dir.as_str());
    }
    // args
    if cfg!(target_os = "windows") {
        command.arg("/C");
    } else {
        command.arg("/C");
    }
    for arg in command_args {
        command.arg(arg.as_str());
    }

    //超时
    let time_out = if let Some(t) = time_out {
        t.clone() as u64
    } else {
        60000u64
    };
    //执行，需要添加超时
    tracing::debug!("local_execute_cli_request开始执行.....",);
    if let Ok(response_result) = timeout(
        Duration::from_millis(time_out),
        Box::pin(async move {
            //发送结果
            //
            _execute_system_command(command, stdout.clone(), stderr.clone()).await
        }),
    )
    .await
    {
        let response = response_result.map_err(|err| anyhow::anyhow!("{}", err))?;
        tracing::debug!("返回数据成功!");
        return Ok(response);
    } else {
        tracing::debug!("请求超时!");
        return Err(anyhow::anyhow!("请求超时!"));
    }
}

async fn _execute_batch_script(
    env: &HashMap<String, String>,
    current: &Option<String>,
    script: String,
    time_out: &Option<u32>,
    stdout: &Option<bool>,
    stderr: &Option<bool>,
) -> anyhow::Result<CommandResult> {
    //保存到临时文件
    let uuid = new_uuid()?;
    let file_name = if cfg!(target_os = "windows") {
        format!("{}.bat", uuid)
    } else {
        format!("{}.sh", uuid)
    };

    let home = rrg01_home_path()?;

    tracing::debug!("{}/{}/{}", home.display(), G01_TEMP_PATH, file_name);
    let res = write_to_file(
        G01_TEMP_PATH,
        file_name.as_str(),
        &script.as_bytes().to_vec(),
    )?;

    let mut command = if cfg!(target_os = "windows") {
        Command::new("cmd")
    } else {
        Command::new("sh")
    };
    //env
    for (key, val) in env {
        command.env(key, val);
    }
    //当前目录
    if let Some(cur_dir) = current {
        command.current_dir(cur_dir.as_str());
    }
    // args
    if cfg!(target_os = "windows") {
        command.arg("/C");
    } else {
        command.arg("/C");
    }

    command.arg(format!("{}/{}/{}", home.display(), G01_TEMP_PATH, file_name).as_str());

    //超时
    let time_out = if let Some(t) = time_out {
        t.clone() as u64
    } else {
        60000u64
    };
    //执行，需要添加超时
    tracing::debug!("local_execute_cli_request开始执行.....",);
    if let Ok(response_result) = timeout(
        Duration::from_millis(time_out),
        Box::pin(async move {
            //发送结果
            //
            _execute_system_command(command, stdout.clone(), stderr.clone()).await
        }),
    )
    .await
    {
        let response = response_result.map_err(|err| anyhow::anyhow!("{}", err))?;
        tracing::debug!("返回数据成功!");
        return Ok(response);
    } else {
        tracing::debug!("请求超时!");
        return Err(anyhow::anyhow!("请求超时!"));
    }
}

async fn _execute_system_command(
    mut command: Command,
    stdout: Option<bool>,
    stderr: Option<bool>,
) -> anyhow::Result<CommandResult> {
    // if let Some(stdout) = &stdout {
    //     if !stdout {
    //         command.stdout(Stdio::null());
    //     } else {
    //         command.stdout(Stdio::piped());
    //     }
    // } else {
    //     command.stdout(Stdio::null());
    // }
    // if let Some(stderr) = &stderr {
    //     if !stderr {
    //         command.stderr(Stdio::null());
    //     } else {
    //         command.stderr(Stdio::piped());
    //     }
    // } else {
    //     command.stderr(Stdio::null());
    // }

    let output = command.output().map_err(|err| anyhow::anyhow!("{}", err))?;
    //
    let status = output.status;
    let stdout = output.stdout;
    let stderr = output.stderr;

    Ok(CommandResult {
        command: vec![],
        code: status.code().map_or(0, |v| v),
        outputs: Some(String::from_utf8_lossy(&stdout).to_string()),
        errors: Some(String::from_utf8_lossy(&stderr).to_string()),
    })
}

async fn _spawn_execute_system_command(
    mut command: Command,
    stdout: Option<bool>,
    stderr: Option<bool>,
) -> anyhow::Result<CommandResult> {
    if let Some(stdout) = &stdout {
        if !stdout {
            command.stdout(Stdio::null());
        } else {
            command.stdout(Stdio::piped());
        }
    } else {
        command.stdout(Stdio::null());
    }
    if let Some(stderr) = &stderr {
        if !stderr {
            command.stderr(Stdio::null());
        } else {
            command.stderr(Stdio::piped());
        }
    } else {
        command.stderr(Stdio::null());
    }
    //
    let child = command.spawn().map_err(|err| anyhow::anyhow!("{}", err))?;

    Err(anyhow::anyhow!("请求超时!"))
}
